package errorx

import (
	"fmt"
	"runtime"
)

// Error 定义了错误接口，包含基本的错误信息和堆栈跟踪
type Error interface {
	error
	fmt.Formatter
	Unwrap() error
	Cause() error
	Code() int
	Type() ErrType
	Stack() Stack
}

// ErrType 定义了错误类型
type ErrType string

const (
	ErrTypeNotFound ErrType = "not_found"
	ErrTypeTimeout  ErrType = "timeout"
	ErrTypeInternal ErrType = "internal"
	ErrTypeInvalid  ErrType = "invalid"
	// TODO: add more error types
)

// Frame 表示堆栈中的一帧
type Frame struct {
	Name string
	File string
	Line int
}

// Stack 表示错误发生时的调用堆栈
type Stack []Frame

// customError 实现了 Error 接口
type customError struct {
	msg    string
	code   int
	errType ErrType
	cause  error
	stack  Stack
}

// Error 实现 error 接口
func (e *customError) Error() string {
	if e.cause != nil {
		return fmt.Sprintf("%s: %v", e.msg, e.cause)
	}
	return e.msg
}

// Format 实现 fmt.Formatter 接口
func (e *customError) Format(f fmt.State, verb rune) {
	switch verb {
	case 'v':
		if f.Flag('+') {
			fmt.Fprintf(f, "%s\n", e.msg)
			if e.cause != nil {
				fmt.Fprintf(f, "Caused by: %v\n", e.cause)
			}
			fmt.Fprintf(f, "Stack:\n")
			for _, frame := range e.stack {
				fmt.Fprintf(f, "  %s\n    %s:%d\n", frame.Name, frame.File, frame.Line)
			}
			return
		}
		fallthrough
	case 's':
		fmt.Fprintf(f, "%s", e.msg)
	}
}

// Unwrap 实现 errors.Unwrap 接口
func (e *customError) Unwrap() error {
	return e.cause
}

// Cause 返回错误的根本原因
func (e *customError) Cause() error {
	return e.cause
}

// Code 返回错误代码
func (e *customError) Code() int {
	return e.code
}

// Type 返回错误类型
func (e *customError) Type() ErrType {
	return e.errType
}

// Stack 返回错误发生时的调用堆栈
func (e *customError) Stack() Stack {
	return e.stack
}

// captureStack 捕获当前的调用堆栈
func captureStack() Stack {
	const depth = 32
	var pcs [depth]uintptr
	n := runtime.Callers(3, pcs[:])
	stack := make(Stack, 0, n)

	frames := runtime.CallersFrames(pcs[:n])
	for {
		frame, more := frames.Next()
		stack = append(stack, Frame{
			Name: frame.Function,
			File: frame.File,
			Line: frame.Line,
		})
		if !more {
			break
		}
	}
	return stack
}

// Wrap 包装一个错误，添加堆栈信息
func Wrap(err error) Error {
	if err == nil {
		return nil
	}
	if e, ok := err.(Error); ok {
		return e
	}
	return &customError{
		msg:    err.Error(),
		code:   500,
		errType: ErrTypeInternal,
		cause:  err,
		stack:  captureStack(),
	}
}

// New 创建一个新的错误
func New(msg string) Error {
	return &customError{
		msg:    msg,
		code:   500,
		errType: ErrTypeInternal,
		stack:  captureStack(),
	}
}

// C 创建一个带有错误代码的新错误
func C(code int, msg string) Error {
	return &customError{
		msg:    msg,
		code:   code,
		errType: ErrTypeInternal,
		stack:  captureStack(),
	}
}

// Cf 创建一个带有错误代码和格式化消息的新错误
func Cf(code int, format string, args ...interface{}) Error {
	return &customError{
		msg:    fmt.Sprintf(format, args...),
		code:   code,
		errType: ErrTypeInternal,
		stack:  captureStack(),
	}
}
